﻿using System;
using System.IO;
using System.Text;
using BizTalk.Archiving.Common;
using BizTalk.Archiving.Common.Configuration;
using Microsoft.BizTalk.Component.Interop;
using Microsoft.BizTalk.Message.Interop;

namespace BizTalk.Archiving.PipelineComponents
{
    /// <summary>
    /// IComponent Implementation
    /// </summary>
    public partial class ArchiveComponent
    {
        public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage inMsg)
        {
            //Check if Archiving is Enabled
            if (IsArchiveToDb || IsArchiveToFile)
            {
                try
                {
                    string fileName = string.Empty;
                    //Get Message Id
                    Guid msgId = inMsg.MessageID;

                    //Get Provider
                    //In the future custom Message Archiver will be supported.
                    var provider = GetProvider(true);

                    //Check if Db Archive is enabled
                    if (IsArchiveToDb)
                    {
                        string xmlStringProperties = GetMessageProperties(inMsg.Context);
                        ArchiveToDb(provider, inMsg.BodyPart.Data, xmlStringProperties, msgId);
                    }

                    //Archive to File
                    if (IsArchiveToFile)
                    {
                        ArchiveToFile(provider, msgId, inMsg.Context, inMsg.BodyPart.Data);
                    }
                }
                catch (Exception exc)
                {
                    System.Diagnostics.EventLog.WriteEntry("BizTalk Message Archiving Component", string.Format("Encountered an error: '{0}' : '{1}'",exc.Message,exc.ToString()), System.Diagnostics.EventLogEntryType.Error);
                }
            }
        
            inMsg.BodyPart.Data.Position = 0;
            return inMsg;
        }

        
        #region Archive Methods
        /// <summary>
        /// Archive To File
        /// </summary>
        /// <param name="provider"></param>
        /// <param name="msgId"></param>
        /// <param name="context"></param>
        /// <param name="msgStream"></param>
        protected virtual void ArchiveToFile(IMessageArchiveProvider provider,Guid msgId, IBaseMessageContext context, Stream msgStream)
        {
            //Get Original FileName using FTP 
            string ftpNs = @"http://schemas.microsoft.com/BizTalk/2003/ftp-properties";
            string fileNs = @"http://schemas.microsoft.com/BizTalk/2003/file-properties";
            string name = "ReceivedFileName";

            //Get Filename by FileAdapter NS
            string fileName = GetPropertyContext(context, name, fileNs);
            if (string.IsNullOrEmpty(fileName))
            {
                //Get Filename by FTP NS
                fileName = GetPropertyContext(context, name, ftpNs); 
            }

            //Get Filename without paths
            FileInfo fInfo = new FileInfo(fileName);
            string origFileName = fInfo.Name;
            string origFileNameExt = fInfo.Extension;
            //Remove Extension
            origFileName = origFileName.Replace(origFileNameExt, "");

            //Update Filename based on macro's
            string archiveFileName = FileArchiveFileName
                .Replace("%SourceFileName%", origFileName)
                .Replace("%MessageID%", msgId.ToString())
                .Replace("%datetime%", DateTime.Now.ToString("yyyy-MM-ddhhmmsstt"))
                .Replace("%time%", DateTime.Now.ToString("hhmmsstt"));

            //Set the fileName to message Id if blank
            if (string.IsNullOrEmpty(archiveFileName))
                archiveFileName = msgId.ToString() + ".part";
            else
            {
                //Apply Extension
                archiveFileName += origFileNameExt;
            }
            provider.ArchiveToFile(archiveFileName, FileArchiveBackupFolder, FileArchiveIsOverwriteFiles, msgStream, FileArchiveUserName, FileArchiveUserPwd, FileArchiveUserDomain, IsCompressFile, CompressionPassword);      
        }

        /// <summary>
        /// Archives the Msg to database
        /// </summary>
        /// <param name="provider"></param>
        /// <param name="msgStream"></param>
        /// <param name="props"></param>
        /// <param name="msgId"></param>
        protected virtual void ArchiveToDb(IMessageArchiveProvider provider, Stream msgStream, string props, Guid msgId)
        {
            //Set Variables
            Stream compressedStream = null;
            long compressedLength = 0;
            long length = 0;

            msgStream.Position = 0;
            length = msgStream.Length;

            //Zip the stream if CompressFile = true
            if (IsCompressFile)
            {
                //Compressed the Stream
                compressedStream = Utility.GetZipStream(
                    Path.Combine(msgId.ToString(), DbFileExtensions), 
                    msgStream, 
                    CompressionPassword);

                compressedLength = compressedStream.Length;
            }
            //Use the uncompressed one
            else
            {
                msgStream.CopyTo(compressedStream);
            }

       
            //Archive to Database
            provider.ArchiveToDb(
                DbConnStr,
                DbConnProvider,
                DbSPName,
                msgId,
                compressedStream,
                props,
                length,
                IsCompressFile,
                compressedLength); 
        }

        /// <summary>
        /// Returns the Message Properties based on DbPropList
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected string GetMessageProperties(IBaseMessageContext context)
        {
            //Get Msg Properties
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("<MessageProperties>");
            //Loop for every properties and retrieve from msg context
            foreach (string p in DbPropList.Split('|'))
            {
                string[] values = p.Split(';');
                if (values.Length == 2)
                {
                    string name = values[0];
                    string ns = values[1];
                    sb.AppendLine(string.Format("<{0}>", name));
                    sb.AppendLine(GetPropertyContext(context, name, ns));
                    sb.AppendLine(string.Format("</{0}>", name));
                }
            }
            sb.AppendLine("</MessageProperties>");
            return sb.ToString();
        }

        #endregion

        #region Helper Methods
        /// <summary>
        /// Retrieves the value of Property from Message Context by Name and namespace
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        protected string GetPropertyContext(IBaseMessageContext context, string name, string ns)
        {
            string result = string.Empty;
            if (context != null)
            {
                try
                {
                    var res = context.Read(name, ns);
                    if (res != null)
                    {
                        result = res.ToString(); 
                    }
                }
                catch
                {
                    //Ignore and return empty string
                }
            }
            return result;
        }

        /// <summary>
        /// Returns the Provider to be used Default or Built-in
        /// </summary>
        /// <param name="isUseCustom"></param>
        /// <returns></returns>
        protected virtual IMessageArchiveProvider GetProvider(bool isUseCustom = false)
        {
            IMessageArchiveProvider provider = null;
            try
            {
                if (isUseCustom)
                {
                    provider = new MessageArchiver();
                }
                else
                {
                    provider = new MessageArchiver();
                }
            }
            catch (Exception exc)
            {
                throw new Exception(string.Format("Unable to create Message Archive Provider, IsUseCustom: '{0}', Message: '{1}', Details: '{2}'", isUseCustom,exc.Message,exc.ToString())); 
            }
            return provider;
        }

        
        #endregion
    }
}
